package com.gitee.qdbp.jdbc.plugins;

import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import com.gitee.qdbp.able.jdbc.model.LikeValue;
import com.gitee.qdbp.able.jdbc.paging.Paging;
import com.gitee.qdbp.jdbc.model.DbVersion;
import com.gitee.qdbp.jdbc.sql.SqlBuffer;

/**
 * 数据库方言处理接口
 *
 * @author zhaohuihua
 * @version 190606
 */
public interface SqlDialect {

    /** 获取数据库版本信息 **/
    DbVersion dbVersion();

    /**
     * IN语句最多支持多少项<br>
     * oracle的IN语句最多只支持1000项, 其他数据库无限制
     * 
     * @return 数量限制, 0表示无限制
     */
    int getInItemLimit();

    /** 生成新的分页SQL对象, 原对象不受影响 **/
    SqlBuffer buildPagingSql(SqlBuffer buffer, Paging paging);

    /** 处理分页, 在原SQL前后追加分页语句 **/
    void processPagingSql(SqlBuffer buffer, Paging paging);

    /** 转换为按拼音排序的表达式 **/
    String toPinyinOrderByExpression(String columnName);

    /** 当前时间的数据库原生写法 **/
    String rawCurrentTimestamp();

    /**
     * Boolean类型的变量转换为字符串(用于拼接SQL)
     * 
     * @param variable 变量
     * @return 转换后的值, true=1, false=0
     */
    String variableToString(Boolean variable);

    /**
     * 字符串类型的变量转换为字符串(用于拼接SQL)<br>
     * 需要替换特殊字符, 并在两侧加单引号
     * 
     * @param variable 变量, 如: Let's go.
     * @return 转换后的值, 如: 'Let''s go.'
     */
    String variableToString(String variable);

    /**
     * 日期类型的变量转换为字符串(用于拼接SQL)<br>
     * 一般需要生成TO_TIMESTAMP SQL语句(MYSQL例外,支持字符串)<br>
     * 
     * @param variable 指定日期
     * @return TO_TIMESTAMP SQL语句<br>
     *         ORACLE: TO_TIMESTAMP('2019-06-01 12:34:56.789', 'YYYY-MM-DD HH24:MI:SS.FF')<br>
     *         MYSQL : '2019-06-01 12:34:56.789'<br>
     */
    String variableToString(Date variable);

    /**
     * 生成LIKE SQL语句<br>
     * 如果fieldValue中包含%等特殊字符, 会当作普通字符处理, 就表示查找带有%的数据, 将会自动生成带ESCAPE的SQL<br>
     * 如: buildLikeSql("xxx"); 表示查找包含xxx的数据, 生成SQL: LIKE ('%'|| 'xxx' ||'%')<br>
     * 如: buildLikeSql("10%"); 表示查找包含10%的数据, 生成SQL: LIKE ('%'|| '10#%' ||'%') ESCAPE '#'<br>
     * 如果希望自己处理ESCAPE逻辑, 可传入LikeValue对象:<br>
     * 如: buildLikeSql(new LikeValue('#', "%10#%")); 表示查找以10%结尾的数据, 生成SQL: LIKE '%10#%' ESCAPE '#'<br>
     * 如: buildLikeSql(new LikeValue("%HOT%COOL%")); 表示查找含有HOT和COOL的数据, 生成SQL: LIKE '%HOT%COOL%'<br>
     * 
     * @param fieldValue 字段值, String或LikeValue
     * @return LIKE SQL语句<br>
     *         ORACLE: LIKE ('%'|| ? ||'%')<br>
     *         MYSQL : LIKE CONCAT('%', ?, '%')<br>
     */
    SqlBuffer buildLikeSql(String fieldValue);

    /**
     * 生成LIKE SQL语句<br>
     * 如: buildLikeSql(new LikeValue('#', "%10#%")); 表示查找以10%结尾的数据, 生成SQL: LIKE '%10#%' ESCAPE '#'<br>
     * 如: buildLikeSql(new LikeValue("%HOT%COOL%")); 表示查找含有HOT和COOL的数据, 生成SQL: LIKE '%HOT%COOL%'<br>
     * 
     * @param fieldValue 字段值, String或LikeValue
     * @return LIKE SQL语句<br>
     *         ORACLE: LIKE ? ESCAPE ?<br>
     *         MYSQL : LIKE ? ESCAPE ?<br>
     */
    SqlBuffer buildLikeSql(LikeValue fieldValue);

    /**
     * 生成LIKE SQL语句
     * 
     * @param fieldValue 字段值, 不支持LikeValue
     * @return LIKE SQL语句<br>
     *         ORACLE: LIKE ( ? ||'%')<br>
     *         MYSQL : LIKE CONCAT( ?, '%')<br>
     */
    SqlBuffer buildStartsWithSql(String fieldValue);

    /**
     * 生成LIKE SQL语句
     * 
     * @param fieldValue 字段值, 不支持LikeValue
     * @return LIKE SQL语句<br>
     *         ORACLE: LIKE ('%'|| ? )<br>
     *         MYSQL : LIKE CONCAT('%', ? )<br>
     */
    SqlBuffer buildEndsWithSql(String fieldValue);

    /**
     * SqlDialect创建接口
     *
     * @author zhaohuihua
     * @version 20201018
     */
    interface Creator {

        /**
         * 根据数据库版本创建SQL方言处理对象
         * 
         * @param version 数据库版本
         * @return SQL方言处理对象
         */
        SqlDialect create(DbVersion version);
    }

    /**
     * SqlDialect创建接口的SimpleSqlDialect实现类
     *
     * @author zhaohuihua
     * @version 20201018
     */
    abstract class CacheCreator implements SqlDialect.Creator {

        // key=DbVersionCode
        private final Map<String, SqlDialect> sqlDialectMaps = new HashMap<>();

        @Override
        public SqlDialect create(DbVersion version) {
            String versionCode = version.toVersionString();
            if (sqlDialectMaps.containsKey(versionCode)) {
                return sqlDialectMaps.get(versionCode);
            } else {
                SqlDialect dialect = newSqlDialect(version);
                sqlDialectMaps.put(versionCode, dialect);
                return dialect;
            }
        }

        /** 根据DbVersion生成SqlDialect **/
        protected abstract SqlDialect newSqlDialect(DbVersion version);
    }
}
